package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTccService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * AccountTccService:使用分布式事务TCC模式
 * try：
 * confirm：事务提交
 * cancel：事务回滚
 */
@Service
@Slf4j
public class AccountTccServiceImpl implements AccountTccService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper freezeMapper;

    /**
     * 根据用户ID扣款,
     * 并往account_freeze_tbl表中插入一条冻结金额的记录
     * @param userId
     * @param money
     */
    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //1.获取seata的事务ID
        String xid = RootContext.getXID();
        //判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,我要拒绝业务
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if(oldFreeze != null){
            //CANCEL执行过,我要拒绝业务
            return;
        }
        //2.扣减可用余额
        accountMapper.deduct(userId,money);
        //3.记录冻结金额,事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freezeMapper.insert(freeze);
    }

    /**
     * 提交事务
     * 并删除冻结金额的记录
     * @param context
     * @return
     */
    @Override
    public boolean confirm(BusinessActionContext context) {
        //1.获取seata的事务ID
        String xid = context.getXid();
        //2.根据id删除冻结记录
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    /**
     * 回滚事务
     * @param context
     * @return
     */
    @Override
    public boolean cancel(BusinessActionContext context) {
        //1.获取seata的事务ID
        String xid = context.getXid();
        //2.查询冻结记录
        AccountFreeze freeze = freezeMapper.selectById(xid);
        //空回滚的判断,判断freeze是否为null,为null证明try没执行,需要空回滚
        if(freeze == null){
            //证明try没执行,需要空回滚
            freeze = new AccountFreeze();
            freeze.setUserId(context.getActionContext("userId").toString());
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            return true;
        }
        //幂等判断
        if(freeze.getState() == AccountFreeze.State.CANCEL){
            //已经处理过一次CANCEL，无需重复处理
            return true;
        }
        //3.恢复可用金额
        String userId = freeze.getUserId();
        Integer freezeMoney = freeze.getFreezeMoney();
        accountMapper.refund(userId,freezeMoney);
        //4.将冻结金额请求,状态该为CANCEL
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}
